from sys import maxint
from core import *
from extensions import *
from adapters import *


#===============================================================================
# int8 -- these must be contained in struct, byte-aligned
#===============================================================================
def UBInt8(name):
    """unsigned big-endian 8-bit integer"""
    return FormatField(name, ">", "B")
def ULInt8(name):
    """unsigned little-endian 8-bit integer"""
    return FormatField(name, "<", "B")
def UNInt8(name):
    """unsigned native-endian 8-bit integer"""
    return FormatField(name, "=", "B")
def SBInt8(name):
    """signed big-endian 8-bit integer"""
    return FormatField(name, ">", "b")
def SLInt8(name):
    """signed little-endian 8-bit integer"""
    return FormatField(name, "<", "b")
def SNInt8(name):
    """signed native-endian 8-bit integer"""
    return FormatField(name, "=", "b")

#===============================================================================
# int16 -- these must be contained in struct, byte-aligned
#===============================================================================
def UBInt16(name):
    """unsigned big-endian 16-bit integer"""
    return FormatField(name, ">", "H")
def ULInt16(name):
    """unsigned little-endian 16-bit integer"""
    return FormatField(name, "<", "H")
def UNInt16(name):
    """unsigned native-endian 16-bit integer"""
    return FormatField(name, "=", "H")
def SBInt16(name):
    """signed big-endian 16-bit integer"""
    return FormatField(name, ">", "h")
def SLInt16(name):
    """signed little-endian 16-bit integer"""
    return FormatField(name, "<", "h")
def SNInt16(name):
    """signed native-endian 16-bit integer"""
    return FormatField(name, "=", "h")

#===============================================================================
# int32 -- these must be contained in struct, byte-aligned
#===============================================================================
def UBInt32(name):
    """unsigned big-endian 32-bit integer"""
    return FormatField(name, ">", "L")
def ULInt32(name):
    """unsigned little-endian 32-bit integer"""
    return FormatField(name, "<", "L")
def UNInt32(name):
    """unsigned native-endian 32-bit integer"""
    return FormatField(name, "=", "L")
def SBInt32(name):
    """signed big-endian 32-bit integer"""
    return FormatField(name, ">", "l")
def SLInt32(name):
    """signed little-endian 32-bit integer"""
    return FormatField(name, "<", "l")
def SNInt32(name):
    """signed native-endian 32-bit integer"""
    return FormatField(name, "=", "l")

#===============================================================================
# int64 -- these must be contained in struct, byte-aligned
#===============================================================================
def UBInt64(name):
    """unsigned big-endian 64-bit integer"""
    return FormatField(name, ">", "Q")
def ULInt64(name):
    """unsigned little-endian 64-bit integer"""
    return FormatField(name, "<", "Q")
def UNInt64(name):
    """unsigned native-endian 64-bit integer"""
    return FormatField(name, "=", "Q")
def SBInt64(name):
    """signed big-endian 64-bit integer"""
    return FormatField(name, ">", "q")
def SLInt64(name):
    """signed little-endian 64-bit integer"""
    return FormatField(name, "<", "q")
def SNInt64(name):
    """signed native-endian 64-bit integer"""
    return FormatField(name, "=", "q")

#===============================================================================
# float -- these must be contained in struct, byte-aligned
#===============================================================================
def BFloat32(name):
    """big-endian 32-bit floating point number"""
    return FormatField(name, ">", "f")
def LFloat32(name):
    """little-endian 32-bit floating point number"""
    return FormatField(name, "<", "f")
def NFloat32(name):
    """native-endian 32-bit floating point number"""
    return FormatField(name, "=", "f")

def BFloat64(name):
    """big-endian 64-bit floating point number"""
    return FormatField(name, ">", "d")
def LFloat64(name):
    """little-endian 64-bit floating point number"""
    return FormatField(name, "<", "d")
def NFloat64(name):
    """native-endian 64-bit floating point number"""
    return FormatField(name, "=", "d")

#===============================================================================
# bit fields -- these must be contained within a BitStruct
#===============================================================================
def Bit(name):
    """a one-bit integer"""
    return BitField(name, 1)
def Nibble(name):
    """a four-bit integer"""
    return BitField(name, 4)
def Octet(name):
    """an eight-bit integer"""
    return BitField(name, 8)

#===============================================================================
# repeaters
#===============================================================================
def GreedyRepeater(subcon):
    """repeats the subcon one or more times"""
    return Repeater(1, maxint, subcon)
def OptionalGreedyRepeater(subcon):
    """repeats the subcon zero or more times"""
    return Repeater(0, maxint, subcon)
def StrictRepeater(count, subcon):
    """repeats the subcon for the `count` times exactly"""
    return Repeater(count, count, subcon)

#===============================================================================
# strings
#===============================================================================
def String(name, length, encoding = None, paddir = None, **kwargs):
    """A string field"""
    s = StringAdapter(Field(name, length), encoding = encoding)
    if paddir is not None:
        s = PaddedStringAdapter(s, length, paddir = paddir, **kwargs)
    return s

def PascalString(name, lengthfield = UBInt8, encoding = None):
    """A pascal-string field (data prefixed by length)"""
    return StringAdapter(
        LengthValueAdapter(
            Struct(name,
                lengthfield("length"),
                MetaField("value", lambda ctx: ctx["length"])
            )
        ),
        encoding = encoding
    )

def CString(name, charsize = 1, terminator = "\x00", encoding = None):
    """A C-string field (data terminated by a terminator char)"""
    return CStringAdapter(
        RepeatUntil(lambda obj, ctx: obj == terminator, 
            Field(name, charsize)
        ),
        encoding = encoding,
        terminator = terminator,
    )

#===============================================================================
# mapping
#===============================================================================
def SymmetricMappingAdapter(subcon, mapping, default = MappingAdapter.NoDefault):
    """defines a symmetrical mapping"""
    reversed_mapping = dict((v, k) for k, v in mapping.iteritems())
    return MappingAdapter(subcon, mapping, reversed_mapping, default, default)

def Enum(subcon, **kw):
    """an enum adapter (symmetrical name-to-value pairs as keyword arguments)"""
    default = kw.pop("_default_", MappingAdapter.NoDefault)
    return SymmetricMappingAdapter(subcon, kw, default)

def FlagsEnum(subcon, **kw):
    """an enum of flags"""
    return FlagsAdapter(subcon, kw)

def Flag(name, truth = 1, falsehood = 0):
    """a boolean indicator"""
    return SymmetricMappingAdapter(
        Bit(name), 
        {True : truth, False : falsehood}
    )


#===============================================================================
# special
#===============================================================================
def Embed(subcon):
    """embeds the subcon (which is supposed to be a Struct) into the 
    encapsulating ('parent') Struct"""
    return Rename("<<EMBED>>", subcon)

def EmbeddedBitStruct(*subcons):
    """An embedded BitStruct"""
    return BitStruct("<<EMBED>>", *subcons)

def IfThenElse(name, predicate, then_subcon, else_subcon):
    """An if-then-else conditional construct: if the predicate indicates True,
    `then_subcon` will be used; otherwise `else_subcon`"""
    return Switch(name, lambda ctx: bool(predicate(ctx)),
        {
            True : then_subcon,
            False : else_subcon,
        }
    )

def If(predicate, subcon, elsevalue = None):
    """A conditional construct"""
    return Switch(subcon.name, lambda ctx: bool(predicate(ctx)),
        {
            True : subcon,
            False : Value(None, lambda ctx: elsevalue)
        }
    )

def LengthValue(name, lengthfield = UBInt8("length"), unit = Field("value", 1)):
    return LengthValueAdapter(
        Struct(name,
            lengthfield,
            MetaRepeater(
                lambda ctx: ctx[lengthfield.name],
                unit
            ),
        ),
        lengthfield.name,
        unit.name,
    )





















